import dayjs, { Dayjs } from 'dayjs'
import 'dayjs/locale/zh-cn'
import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear'
import isLeapYear from 'dayjs/plugin/isLeapYear'
import isoWeek from 'dayjs/plugin/isoWeek'

import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
import { isNullOrUndefined } from './BaseUtil'
dayjs.locale('zh-cn')
dayjs.extend(isSameOrAfter)
dayjs.extend(isoWeeksInYear)
dayjs.extend(isLeapYear)
dayjs.extend(isoWeek)

/**
 * # 日期时间格式化风格
 */
export enum DateTimeFormatStyleEnum {
  /**
   * 年-月-日
   */
  YYYY_MM_DD = 'YYYY-MM-DD',
  /**
   * 年/月/日
   */
  YYYY_MM_DD_2 = 'YYYY/MM/DD',
  /**
   * 年月日
   */
  YYYYMMDD = 'YYYYMMDD',
  /**
   * 年-月
   */
  YYYY_MM = 'YYYY-MM',
  /**
   * 年月
   */
  YYYYMM = 'YYYYMM',
  YYYY = 'YYYY',
  MM = 'MM',
  /**
   * 日
   */
  DD = 'DD'
}
/**
 * # 默认日期格式: YYYY-MM-DD
 */
const DEFAULT_DATE_FORMAT = DateTimeFormatStyleEnum.YYYY_MM_DD
/**
 * 获取指定日期的iso周
 * @param dateStr 日期时间字符串. 不设置时，基于当前操作系统的日期时间
 * @param format dateStr值的日期字符串格式. 默认: YYYY-MM-DD
 * @returns
 */
export function getIsoWeek(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
): number {
  return buildDayjs(dateStr, format).isoWeek()
}
/**
 * # 判断date是否在curDate之后（date相对于curDate是否为未来日期）[当前日期算过去日期]
 * ---
 * - 如: date为 2024-10-02， curDate为 2024-01-01, 则为true
 * - 如: date为 2024-10-01， curDate为 2024-01-02, 则为false
 * - 如: date为 2024-10-01， curDate为 2024-01-01, 则为false
 *
 * @param date 特定日期字符串
 * @param curDate 当前日期字符串. 默认为：当前操作系统的日期时间
 * @param dateFormat 日期格式. 默认为: YYYY-MM-DD
 */
export function isFutureDate(
  date: string,
  curDate?: string | Dayjs,
  dateFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = buildDayjs(date, dateFormat)
  const cdj = dayjs.isDayjs(curDate) ? curDate : buildDayjs(curDate, dateFormat)
  return dj.isAfter(cdj)
}

/**
 * # 获取指定日期的iso周
 * ---
 * - retUseSplitline为true时，值格式: 2024-30
 * - retUseSplitline为true时，值格式: 202430
 *
 * @param dateStr 日期时间字符串. 不设置时，基于当前操作系统的日期时间
 * @param format dateStr值的日期字符串格式. 默认: YYYY-MM-DD
 * @param retUseSplitline 结果中的年月是否通过`-`进行分割. 默认: true
 * @returns
 */
export function getIsoWeekYYYYMM(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retUseSplitline: boolean = true
): string {
  const dj = buildDayjs(dateStr, format)
  const tmp = dj.isoWeek()
  const isoWeek = tmp < 10 ? `0${tmp}` : `${tmp}`
  if (retUseSplitline) {
    return `${dj.get('year')}-${isoWeek}`
  } else {
    return `${dj.get('year')}${isoWeek}`
  }
}

/**
 * # 基于本地操作系统的时间，获取档期日期
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 * @returns
 */
export function getCurrentDateBaseLocalaOperateSystem(
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
): string {
  return buildDayjs().format(retFormat)
}

/**
 * # 构建dayjs实例
 * @param dateTimeStr 日期时间字符串. 不设置时，基于当前操作系统的日期时间
 * @param format dateStr值的日期字符串格式. 默认: YYYY-MM-DD
 * @returns
 */
export function buildDayjs(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const ret = dateStr ? dayjs(dateStr, format) : dayjs()
  return ret
}

/**
 * # 获取指定日期的昨天
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 */
export function getYestoday(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = buildDayjs(dateStr, format)
  return dj.subtract(1, 'day').format(retFormat)
}

/**
 * # 获取指定日期的明天
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 */
export function getTomorrow(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.add(1, 'day').format(retFormat)
}

/**
 * # 基于指定日期时间，获取该日期所在ISO周的第一天日期
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 */
export function getISOWeekFirstDay(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.startOf('week').format(retFormat)
}

/**
 * # 基于指定日期时间，获取该日期所在ISO周的最后一天日期
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 *
 */
export function getISOWeekLastDay(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.endOf('week').format(retFormat)
}

/**
 * # 基于指定日期时间，获取该日期所在月的第一天日期
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 */
export function getMonthFirstDay(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.startOf('month').format(retFormat)
}

/**
 * # 基于指定日期时间，获取该日期所在月的第一天日期
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 */
export function getMonthLastDay(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.endOf('month').format(retFormat)
}

/**
 * # 基于指定日期时间，获取该日期所在年的第一天日期
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 */
export function getYearFirstDay(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.startOf('year').format(retFormat)
}

/**
 * # 基于指定日期时间，获取该日期所在年的最后一天日期
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 */
export function getYearLastDay(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.endOf('year').format(retFormat)
}

/**
 * # 获取指定日期加N天后的日期
 *
 * 如: 当前日期为: 1月1日，加1天之后，值为: 1月2日
 * @param nDay 加N天
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 * @returns
 */
export function dayAdd(
  nDay: number,
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.add(nDay, 'day').format(retFormat)
}

/**
 * # 获取指定日期减N天后的日期
 *
 * 如: 当前日期为: 1月2日，加1天之后，值为: 1月1日
 * @param nDay 减N天
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 * @returns
 */
export function daySubtract(
  nDay: number,
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.subtract(nDay, 'day').format(retFormat)
}

/**
 * # 获取指定日期减N年后的年
 *
 * 如: 当前日期为: 2024-11-20，减少5年，则为2020年
 * @param nYear 减N年
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @returns
 */
export function yearSubtract(
  nYear: number,
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
): number {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.subtract(nYear, 'year').get('year')
}

/**
 * # 获取指定日期的上月26日日期
 *
 * 如: 当前日期为: 2月2日，则上月26日为：1月26日
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 * @returns
 */
export function prevMonth26(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.subtract(1, 'month').date(26).format(retFormat)
}

/**
 * # 获取指定日期的当月27日日期
 *
 * 如: 当前日期为: 2月2日，则当月27日为：1月27日
 * @param dateStr 日期时间字符串. 不设置的时候，基于当前操作系统的日期时间
 * @param format dateStr值的日期时间字符串格式. 默认: YYYY-MM-DD
 * @param retFormat 返回值的日期格式. 默认: YYYY-MM-DD
 * @returns
 */
export function curMonth27(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT,
  retFormat: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const dj = dateStr ? dayjs(dateStr, format) : dayjs()
  return dj.date(27).format(retFormat)
}
/**
 * # 判断dateStr是星期几
 * ---
 * 如: 2024-09-30 --> 周一
 * @param dateStr 待判断的日期字符串
 * @param format 日期格式
 * @returns
 */
export function getWeekDay(
  dateStr?: string,
  format: DateTimeFormatStyleEnum = DEFAULT_DATE_FORMAT
) {
  const datas = dateStr ? dayjs(dateStr, format).day() : dayjs().day()
  const week = ['日', '一', '二', '三', '四', '五', '六']
  return '周' + week[datas]
}
/**
 * # 将日期转换为对应数值
 * ---
 * - 如: 2024-01-01 --> 20240101
 * - 如: 2024/01/01 --> 20240101
 * @param date 日期数据. 支持的格式: 2020401, 2024-04-01, 2024/04/01
 * @returns
 */
export function dateConvert(date: string | number) {
  if (isNullOrUndefined(date)) {
    return date
  }
  return parseInt(`${date}`.replace(/[-/]/g, ''))
}
